<?php

namespace app\service;
// +----------------------------------------------------------------------
// | bbcThink
// +----------------------------------------------------------------------
// | Copyright (c) 2025 http://bbcweb.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: bbcweb <22745529@qq.com>
// +----------------------------------------------------------------------
use think\facade\Db;
use think\facade\Cache;

/**
 * 微信服务层
 * @datetime 2016-12-01T21:51:08+0800
 */
class WeixinService
{    
    //公众号消息通知
    public static function WeixinMPMessage($params = [])
    {
        //买家订单付款通知管理员
        if ($params['type'] == 1) {
        }
        //发货时推送信息给用户
        if ($params['type'] == 2) {
        }
        //用户发起退款时推送给商城管理人员
        if ($params['type'] == 3) {
        }
        //买家支付成功，通知买家
        if ($params['type'] == 5) {

            self::sendwxmsg('openid', [], 5);
        }
        //买家订单退款成功，通知买家
        if ($params['type'] == 6) {
        }
    }

    public static function getConfig($id = 1)
    {
        $wxonfig = ''; //cache('weixin_config'.$id);
        if (empty($wxonfig)) {
            $weixin_config = Db::name('weixin')->where(['id' => $id, 'status' => 1])->find();
            if (!$weixin_config) {
                return null;
            }
            $wxonfig = [
                'appid' => $weixin_config['appid'],
                'appsecret' => $weixin_config['appsecret'],
                'mchid' => $weixin_config['mchid'],
                'mchsecret' => $weixin_config['mchsecret']
            ];
            cache('weixin_config' . $weixin_config['id'], $wxonfig, 86400);
        }
        return $wxonfig;
    }
    /**
     * 用户付款订单通知,通知管理员
     */
    public static function noticePayOrderToGM($openid, $data)
    {
        $time = date("Y-m-d H:m:s", time());

        $template = array(
            'touser' => $openid,
            'template_id' => 'osqghbN0Hsw-5hpHHkelXnrDkp-yu0VApuErK6nojB8', //"
            'url' => isset($data['url']) ? $data['url'] : '',
            'data' => array(
                'first' => array('value' => urlencode($data['title']), 'color' => "#FF0000"),
                'keyword1' => array('value' => urlencode($data['goods']['title'] . $data['goods']['spec'])),
                'keyword2' => array('value' => urlencode($data['total_price'])),
                'keyword3' => array('value' => urlencode($data['address']['name'])),
                'keyword4' => array('value' => urlencode($data['add_time'])),
                'keyword5' => array('value' => urlencode($data['sn'])),
                'remark'  => array('value' => urlencode($data['memo'])),
            )
        );
        $json_template = json_encode($template);
        return $json_template;
    }
    public static function setWeixinAcesstoken($token)
    {
        $wxData = self::getConfig();
        $appid = $wxData['appid'];
        $secret = $wxData['appsecret'];
        $key = $appid . '_access_token';
        $result['access_token'] = $token;
        Cache::set($key, $result, 7000);
    }
    public static function getWeixinAcesstoken($get_now = 0)
    {
        $wxData = self::getConfig();
        $appid = $wxData['appid'];
        $secret = $wxData['appsecret'];
        $key = $appid . '_access_token';
        $result = Cache::get($key);
        if (!empty($result) && $get_now == 0) {
            if (isset($result['expires_time']) && $result['expires_time'] > time()) {
                //$access_token2 = $result['access_token'];
                //return $access_token2;
            }
        }

        $json_token = self::curl_post("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" . $appid . "&secret=" . $secret . "");
        $result = json_decode($json_token, true);
        $result['expires_time'] = time() + 7100;
        Cache::set($key, $result, 7100);
        $access_token2 = $result['access_token'];
        return $access_token2;
    }

    public static function sendwxmsg($openid = '', $data = [], $msgtype = 1)
    {
        //模板消息
        switch ($msgtype) {
            case 1: //买家订单付款通知
                $json_template = self::noticePayOrderToGM($openid, $data);
                break;
            case 2: //发货时推送信息给用户
                $json_template = '';
                break;
            case 3: //用户申请订单退款,通知管理员
                $json_template = '';
                break;
            case 4: //代理金额不足提示
                $json_template = '';
                break;
            case 5: //买家购买成功
                $json_template = '';
                break;
            case 6: //买家退款成功
                $json_template = '';
                break;
            case 20: //买家修改订单收货地址
                $json_template = '';
                break;
            default:
                return DataReturn('没有消息模板ID', -1);               
        }
        $url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=" . self::getWeixinAcesstoken();
        $res = self::curl_post($url, urldecode($json_template));        
        return json_decode($res, true);
    }
    /**
     * [ReturnData 返回数据统一格式]
     * @datetime 2018-10-06T16:54:24+0800
     * @param    [array]                   $data [返回数据]
     */
    private function ReturnData($data)
    {
        // 参数处理
        $out_trade_no = substr($data['out_trade_no'], 0, strlen($data['out_trade_no']) - 6);

        // 返回数据固定基础参数
        $data['trade_no']       = $data['transaction_id'];  // 支付平台 - 订单号
        $data['buyer_user']     = $data['openid'];          // 支付平台 - 用户
        $data['out_trade_no']   = $out_trade_no;            // 本系统发起支付的 - 订单号
        $data['subject']        = $data['attach'];          // 本系统发起支付的 - 商品名称
        $data['pay_price']      = $data['total_fee'] / 100;   // 本系统发起支付的 - 总价
        return $data;
    }
    /**
     * 签名生成
     * @date    2019-01-07
     * @desc    description
     * @param   [array]           $params [输入参数]
     */
    private function GetSign($params = [], $key = '')
    {
        ksort($params);
        $sign  = '';
        foreach ($params as $k => $v) {
            if ($k != 'sign' && $v != '' && $v != null) {
                $sign .= "$k=$v&";
            }
        }       
        return strtoupper(md5($sign . 'key=' . $key));
    }
    /**
     * 判断字符串是否为xml格式
     * @date    2019-01-07
     * @desc    description
     * @param   [string]          $string [字符串]
     */
    static function XmlParser($string)
    {
        $xml_parser = xml_parser_create();
        if (!xml_parse($xml_parser, $string, true)) {
            xml_parser_free($xml_parser);
            return false;
        } else {
            return (json_decode(json_encode(simplexml_load_string($string)), true));
        }
    }
    /**
     * xml转数组
     * @date    2019-01-07
     * @desc    description
     * @param   [string]          $xml [xm数据]
     */
    static function XmlToArray($xml)
    {
        if (!self::XmlParser($xml)) {
            return is_string($xml) ? $xml : '接口返回数据有误';
        }

        return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
    }
    /**
     * 数组转xml
     * @date    2019-01-07
     * @desc    description
     * @param   [array]          $data [数组]
     */
    static function ArrayToXml($data)
    {
        $xml = '<xml>';
        foreach ($data as $k => $v) {
            $xml .= '<' . $k . '>' . $v . '</' . $k . '>';
        }
        $xml .= '</xml>';
        return $xml;
    }

    static function curl_post($url, $data = array())
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
        // POST数据
        curl_setopt($ch, CURLOPT_POST, 1);
        // 把post的变量加上
        curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
        $output = curl_exec($ch);
        curl_close($ch);
        return $output;
    }
    /**
     * [HttpRequest 网络请求]
     * @datetime 2017-09-25T09:10:46+0800
     * @param    [string]          $url         [请求url]
     * @param    [array]           $data        [发送数据]
     * @param    [boolean]         $use_cert    [是否需要使用证书]
     * @param    [int]             $second      [超时]
     * @return   [mixed]                        [请求返回数据]
     */
    function HttpRequest($url, $data, $use_cert = false, $second = 30)
    {
        $options = array(
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HEADER         => false,
            CURLOPT_POST           => true,
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_SSL_VERIFYHOST => false,
            CURLOPT_POSTFIELDS     => $data,
            CURLOPT_TIMEOUT        => $second,
        );
        $ch = curl_init($url);
        curl_setopt_array($ch, $options);
        $result = curl_exec($ch);
        //返回结果
        if ($result) {
            curl_close($ch);
            return $result;
        } else {
            $error = curl_errno($ch);
            curl_close($ch);
            return "curl出错，错误码:$error";
        }
    }

    /**
     * Notes: 生成sign
     * @param $arr
     * @param $secret
     * @return string
     */
    static function make_sign($arr, $secret)
    {
        //签名步骤一：按字典序排序参数
        ksort($arr);
        $str = self::to_url_params($arr);

        //签名步骤二：在str后加入KEY
        $str = $str . "&key=" . $secret;
        //签名步骤三：HMAC-SHA256 类型  加密的字符串 key是商户秘钥
        $str = hash_hmac('sha256', $str, $secret); //$this->sp_mchsecret);
        //签名步骤四：所有字符转为大写
        $result = strtoupper($str);
        return $result;
    }
    /**
     * Notes: 数组转字符串
     * @param $arr
     * @return string
     */
    static function to_url_params($arr)
    {
        $str = "";

        foreach ($arr as $k => $v) {

            if (!empty($v) && ($k != 'sign')) {
                $str .= "$k" . "=" . $v . "&";
            }
        }

        $str = rtrim($str, "&");
        return $str;
    }
    /**
     * Notes: 数组转XML
     * @param $arr
     * @return string
     */
    static function array_to_xml($arr)
    {
        $xml = '<?xml version="1.0" encoding="UTF-8"?><xml>';
        foreach ($arr as $key => $val) {
            $xml .= "<" . $key . ">$val</" . $key . ">";
        }
        $xml .= "</xml>";
        return $xml;
    }
    /**
     * Notes: XML转数组
     * @param $xml
     * @return mixed
     */
    static function xml_to_array($xml)
    {
        libxml_disable_entity_loader(true);
        $arr = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        return $arr;
    }
    /**
     * Notes: POST 请求 此处需要证书
     * @param $xml
     * @param $url
     * @param int $second
     * @return bool|string
     */
    static function curl_post_ssl($url, $vars, $second = 30, $aHeader = array())
    {
        $isdir = str_replace('public/', '', ROOT_PATH) . "partnerwxcert/";
        $ch = curl_init(); //初始化curl
        curl_setopt($ch, CURLOPT_TIMEOUT, $second); //设置执行最长秒数
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //要求结果为字符串且输出到屏幕上
        curl_setopt($ch, CURLOPT_URL, $url); //抓取指定网页
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 终止从服务端进行验证
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); //
        curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM'); //证书类型
        curl_setopt($ch, CURLOPT_SSLCERT, $isdir . 'apiclient_cert.pem'); //证书位置
        curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM'); //CURLOPT_SSLKEY中规定的私钥的加密类型
        curl_setopt($ch, CURLOPT_SSLKEY, $isdir . 'apiclient_key.pem'); //证书位置
        //        curl_setopt($ch, CURLOPT_CAINFO, 'PEM');
        //        curl_setopt($ch, CURLOPT_CAINFO, $isdir . 'rootca.pem');
        if (count($aHeader) >= 1) {
            curl_setopt($ch, CURLOPT_HTTPHEADER, $aHeader); //设置头部
        }
        curl_setopt($ch, CURLOPT_POST, 1); //post提交方式
        curl_setopt($ch, CURLOPT_POSTFIELDS, $vars); //全部数据使用HTTP协议中的"POST"操作来发送

        $data = curl_exec($ch); //执行回话
        if ($data) {
            curl_close($ch);
            return $data;
        } else {
            $error = curl_errno($ch);
            echo "call faild, errorCode:$error\n";
            curl_close($ch);
            return false;
        }
    }
    private static function getV3Sign($url, $http_method, $body)
    {
        $mchid = '';
        $nonce = strtoupper(createNonceStr(32));
        $serialNo = '';
        $timestamp = time();
        $url_parts = parse_url($url);
        //$canonical_url = ($url_parts['path'] . (!empty($url_parts['query']) ? "?${url_parts['query']}" : ""));
        $canonical_url = ($url_parts['path'] . (!empty($url_parts['query']) ? $url_parts['query'] : ""));
        $sslKeyPath = '../wxcert/apiclient_key.pem';
        //拼接参数
        $message = $http_method . "\n" .
            $canonical_url . "\n" .
            $timestamp . "\n" .
            $nonce . "\n" .
            $body . "\n";
        $private_key = self::getPrivateKey($sslKeyPath);
        openssl_sign($message, $raw_sign, $private_key, 'sha256WithRSAEncryption');
        $sign   = base64_encode($raw_sign);
        $token = sprintf('WECHATPAY2-SHA256-RSA2048 mchid="%s",nonce_str="%s",timestamp="%s",serial_no="%s",signature="%s"', $mchid, $nonce, $timestamp, $serialNo, $sign);
        return $token;
    }

    public static function getPrivateKey($filepath = '')
    {
        if (empty($filepath)) {
            $filepath = '../wxcert/apiclient_key.pem';
        }
        return openssl_get_privatekey(file_get_contents($filepath));
    }
}
